package gee

import (
	"fmt"
	"net/http"
)

type HandlerFunc func(*Context)
type router struct {
	pathNodes map[string]*pathNode
	handlers  map[string]map[string]HandlerFunc
}

func NewRouter() *router {

	return &router{
		pathNodes: map[string]*pathNode{
			http.MethodGet:  NewRootNode(),
			http.MethodPost: NewRootNode(),
		},
		handlers: map[string]map[string]HandlerFunc{
			http.MethodGet:  make(map[string]HandlerFunc),
			http.MethodPost: make(map[string]HandlerFunc),
		},
	}
}

func (r *router) AddRouter(method string, path string, handler HandlerFunc) error {

	if _, ok := r.pathNodes[method]; !ok {
		return fmt.Errorf("invalid method: %s", method)
	}
	r.pathNodes[method].AddPath(path)
	r.handlers[method][path] = handler
	return nil
}

func handleNotFound(c *Context) {

	c.handlers = append(c.handlers, func(c *Context) {
		c.SetStatusCode(http.StatusNotFound)
		c.Resp.Write([]byte("Path Not Found"))
	})
	c.Next()
}

func handleFound(c *Context, handler HandlerFunc) {

	c.handlers = append(c.handlers, handler)
	c.Next()
}

func (r *router) Handle(c *Context) {

	rootNode, ok := r.pathNodes[c.Method]
	if !ok {
		handleNotFound(c)
		return
	}
	nodeMatches := rootNode.MatchByPath(c.Path)
	if len(nodeMatches) == 0 {
		handleNotFound(c)
		return
	}

	handlerFuncs, ok := r.handlers[c.Method]
	if !ok {
		handleNotFound(c)
		return
	}
	for _, match := range nodeMatches {
		handler, ok := handlerFuncs[match.node.path]
		if !ok {
			continue
		}

		for k, v := range match.params {
			c.Params[k] = v
		}
		handleFound(c, handler)
		return
	}

	handleNotFound(c)
}
